﻿using System;
using System.Collections.Generic;
using System.IO;
using System.Reflection;

namespace DotNetToTypeScriptExporter
{
    public class Program
    {       
        static void Main(string[] args)
        {            
            string announce = ".NET to TypeScript Exporter version " + Assembly.GetExecutingAssembly().GetName().Version.ToString(2);
            Console.WriteLine("\n" + announce + "\n" + new string('=', announce.Length) + "\r\n");
            
            ConsoleCommandLine commandLine = new ConsoleCommandLine();
            commandLine.RegisterParameter(new CommandLineParameter("ignorecase", false, "Ignore case of type names (ie System.String == system.string)"));
            commandLine.RegisterParameter(new CommandLineParameter("nofields", false, "Don't export public fields")); 
            commandLine.RegisterParameter(new CommandLineParameter("noproperties", false, "Don't export public properties"));
            commandLine.RegisterParameter(new CommandLineParameter("nomethods", false, "Don't export public methods")); 
            commandLine.RegisterParameter(new CommandLineString("output", true, "The path to the output .ts file"));
            commandLine.RegisterParameter(new CommandLineString("assemblies", true, "A list of one or more paths to the assemblies, separated by commas, from which types are to be exported"));
            commandLine.RegisterParameter(new CommandLineParameter("exportall", false, "If included, exports all types in the assembly"));
            commandLine.RegisterParameter(new CommandLineString("types", false, "A list of one or more fully qualified names of types, separated by commas, in the specified assembly to export"));
            commandLine.Parse(args);

            string outputPath = GetAbsolutePath(commandLine["output"].Value);
            if (!CheckPath(outputPath)) return;

            List<string> assemblyPaths = new List<string>();
            foreach (string assemblyPath in commandLine["assemblies"].Value.Replace(", ", ",").Split(','))
            {
                if (!CheckPath(assemblyPath))
                {
                    Console.WriteLine("One or more of your assembly paths is invalid. Please check and try again");
                    return;
                }
                else
                {
                    assemblyPaths.Add(GetAbsolutePath(assemblyPath));
                }
            }

            Console.WriteLine("Beginning export...\nOutput path: " + outputPath);

            try
            {
                if (commandLine["exportall"].Exists || !commandLine["types"].Exists)
                {
                    DotNetToTypeScriptExporter exporter = new DotNetToTypeScriptExporter(outputPath, assemblyPaths, null, commandLine["ignorecase"].Exists, !commandLine["nofields"].Exists, 
                        !commandLine["noproperties"].Exists, !commandLine["nomethods"].Exists);
                    exporter.OnTypeProcessed += ShowTypeProcessed;
                    exporter.OnTypeSkipped += ShowTypeSkipped;
                    exporter.Export();
                }
                else if (commandLine["types"].Exists)
                {
                    string[] typeNames = (commandLine["types"].Value).Replace(", ", ",").Split(',');
                    DotNetToTypeScriptExporter exporter = new DotNetToTypeScriptExporter(outputPath, assemblyPaths, typeNames, commandLine["ignorecase"].Exists, !commandLine["nofields"].Exists, 
                        !commandLine["noproperties"].Exists, !commandLine["nomethods"].Exists);
                    exporter.OnTypeProcessed += ShowTypeProcessed;
                    exporter.OnTypeSkipped += ShowTypeSkipped;
                    exporter.Export();
                }
            }
            catch (Exception ex)
            {
                Console.WriteLine("Exception occurred during export: " + ex.Message);
                return;
            }

            Console.WriteLine("Conversion complete.");
        }

        private static void ShowTypeProcessed(Type type)
        {
            Console.WriteLine("Processed type " + type.FullName);
        }

        private static void ShowTypeSkipped(Type type, bool notPublic, bool notClassOrInterface)
        {
            Console.WriteLine("Skipped type " + type.FullName + " - " + (notPublic ? "not public" : "") + (notClassOrInterface ? (notPublic ? ", " : "") + "not a class or interface type" : ""));
        }

        private static bool CheckPath(string path)
        {
            try
            {
                if (!File.Exists(path)) 
                {
                    Console.WriteLine("Error - specified path is invalid. File does not exist.");
                    return false;
                }
            }
            catch
            {
                Console.WriteLine("Error - specified path is invalid. You must supply a complete, valid path.");
                return false;
            }

            return true;
        }
        
        private static string GetAbsolutePath(string path)
        {            
            // remove double quotes if present (protects paths with spaces)
            path = path.TrimStart('"').TrimEnd('"');

            // local paths must be exported to absolutes, as we'll be calling Assembly.LoadFile
            if (!Path.IsPathRooted(path))
                path = Path.Combine(Environment.CurrentDirectory, path);
            return path;
        }
    }
}
